package org.openea.eap.module.obpm.service.eap;

import cn.hutool.core.util.IdUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.openea.eap.framework.common.enums.CommonStatusEnum;
import org.openea.eap.framework.common.exception.ServiceException;
import org.openea.eap.framework.common.util.servlet.ServletUtils;
import org.openea.eap.framework.tenant.core.context.TenantContextHolder;
import org.openea.eap.module.obpm.config.EmailApiConfig;
import org.openea.eap.module.obpm.entity.EmailApiResponse;
import org.openea.eap.module.obpm.service.obpm.ObmpClientService;
import org.openea.eap.module.obpm.service.singlelogin.model.LoginResponse;
import org.openea.eap.module.obpm.service.singlelogin.service.LoginService;
import org.openea.eap.module.system.controller.admin.auth.vo.AuthLoginReqVO;
import org.openea.eap.module.system.controller.admin.auth.vo.AuthLoginRespVO;
import org.openea.eap.module.system.convert.auth.AuthConvert;
import org.openea.eap.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import org.openea.eap.module.system.dal.dataobject.user.AdminUserDO;
import org.openea.eap.module.system.dal.mysql.oauth2.OAuth2AccessTokenMapper;
import org.openea.eap.module.system.dal.mysql.user.AdminUserMapper;
import org.openea.eap.module.system.dal.redis.oauth2.OAuth2AccessTokenRedisDAO;
import org.openea.eap.module.system.enums.logger.LoginLogTypeEnum;
import org.openea.eap.module.system.enums.logger.LoginResultEnum;
import org.openea.eap.module.system.enums.oauth2.OAuth2ClientConstants;
import org.openea.eap.module.system.service.auth.AdminAuthService;
import org.openea.eap.module.system.service.auth.AdminAuthServiceImpl;
import org.openea.eap.module.system.service.oauth2.OAuth2ClientService;
import org.openea.eap.module.system.service.user.AdminUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static org.openea.eap.framework.common.exception.util.ServiceExceptionUtil.exception;
import static org.openea.eap.module.system.enums.ErrorCodeConstants.*;

@Service
@Primary
@Slf4j
public class ObpmAuthServiceImpl extends AdminAuthServiceImpl implements AdminAuthService {

    @Resource
    private ObmpClientService obmpClientService;

    @Resource
    private OAuth2ClientService oauth2ClientService;

    @Resource
    @Lazy
    ObpmUserServiceImpl obpmUserService;


    @Resource
    private OAuth2AccessTokenMapper oauth2AccessTokenMapper;

    @Resource
    private OAuth2AccessTokenRedisDAO oauth2AccessTokenRedisDAO;


    @Autowired
    RedisTemplate<String, String> stringRedisTemplate;

    @Autowired
    private LoginService loginService;
    @Resource
    protected AdminUserService userService;
    @Resource
    protected AdminUserMapper userMapper;
    @Autowired
    private EmailApiConfig emailApiConfig;

    @Override
    public AdminUserDO authenticate(String username, String password) {
        final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME;
        // 校验账号是否存在
        AdminUserDO user = userService.getUserByUsername(username);
        if (user == null) {
            try {
                //可增加实时查询未同步用户
                JSONObject jsonUser = queryObpmUser(username, true);
                // 创建用户并保存密码
                if (jsonUser != null) {
                    // todo 出现重复数据，暂时关闭11
                    // user = createAdminUser(jsonUser, "obpm-init");
                }
            } catch (Exception e) {
                log.error(String.format("queryObpmUser fail, user=%s, exception=%s", username, e.getMessage()));
                createLoginLog(null, username, logTypeEnum, LoginResultEnum.THIRD_FAIL);
                throw exception(AUTH_THIRD_LOGIN_FAIL);
            }
        }
        if (user == null) {
            createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
            countIpPwdWrong();
            throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
        }
        if (!userService.isPasswordMatch(password, user.getPassword())) {
            createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
            countIpPwdWrong();
            throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
        }
        // 校验是否禁用
        if (CommonStatusEnum.isDisable(user.getStatus())) {
            createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.USER_DISABLED);
            throw exception(AUTH_LOGIN_USER_DISABLED);
        }
        return user;
    }

    @Override
    public AuthLoginRespVO login(AuthLoginReqVO reqVO) {
        // 校验验证码
        //validateCaptcha(reqVO);

        // 使用账号密码，进行登录
        AdminUserDO user = authenticate(reqVO.getUsername(), reqVO.getPassword());

        // 如果 socialType 非空，说明需要绑定社交用户
//        if (reqVO.getSocialType() != null) {
//            socialUserService.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
//                    reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()));
//        }
        // 创建 Token 令牌，记录登录日志
        return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME);
    }

    public AuthLoginRespVO loginByToken(com.alibaba.fastjson.JSONObject reqVO, String ip) {
        // 校验验证码
        //validateCaptcha(reqVO);

        String token = reqVO.getString("token");

        // 校验token不能为空
        if (StringUtils.isBlank(token)) {
            log.error("token为空");
            throw new ServiceException(500, "登录过期");
        }

        // 校验token是否正确
        LoginResponse login = loginService.login(token, ip);
        if (login == null) {
            throw new ServiceException(500, "登录过期");
        }

        LoginResponse.Account accountResponse = login.getAccount();
        if (accountResponse == null) {
            throw new ServiceException(500, "登录过期");
        }

        String account = accountResponse.getCode();

        log.info("单点登录解析的account:{}", account);

        if (StringUtils.isBlank(account)) {
            log.error("token过期");
            throw new ServiceException(500, "登录过期");
        }


        // 查询人员信息
        Map<String, Object> map = queryUserFromOldDb(account);

        if (map == null) {
            if (!checkEmployee(account)) {
                throw new ServiceException(500, "您目前不在人才库名单内，如需创建账号，请联系管理员");
            }
            insertUserToDb(accountResponse);
        }

        // 登录成功

        // 查新版后端数据库
        AdminUserDO user = userService.getUserByUsername(account);
        if (user == null) {
            insertUserToEapDb(accountResponse);
            user = userService.getUserByUsername(account);
        }
//        if (user == null) {
//            throw new ServiceException(500, "未知错误，请联系管理员");
//        }

        // 创建 Token 令牌，记录登录日志
        return createTokenAfterLoginSuccess(user.getId(), account, LoginLogTypeEnum.LOGIN_USERNAME);
    }

    private Boolean checkEmployee(String account) {
        JdbcTemplate obpmJdbcTemplate = obmpClientService.getObpmJdbcTemplate();
        String sql = "SELECT COUNT(*) FROM org_employee WHERE account = ? AND active = 1";
        Integer count = obpmJdbcTemplate.queryForObject(sql, Integer.class, account);
        return count > 0;
    }

    private Map<String, Object> queryUserFromOldDb(String account) {
        JdbcTemplate obpmJdbcTemplate = obmpClientService.getObpmJdbcTemplate();
        String sql = "SELECT * FROM org_user WHERE account_ = ?";
        Map<String, Object> map = null;
        try {
            map = obpmJdbcTemplate.queryForMap(sql, account);
        } catch (Exception e) {
            log.error("找不到工号===>{}", account);
            e.printStackTrace();
        }

        return map;
    }

    public void insertUserToDb(LoginResponse.Account accountResponse) {
        JdbcTemplate obpmJdbcTemplate = obmpClientService.getObpmJdbcTemplate();
        LocalDateTime now = LocalDateTime.now();
        String sexCode = accountResponse.getSexCode();
        Integer sex = null;
        if (sexCode != null) {
            if (sexCode.equals("MALE")) {
                sex = 1;
            } else {
                sex = 2;
            }
        }
        String account = accountResponse.getCode();

        String email = queryEmailByAccount(account);

        String sql = "INSERT INTO org_user (id_, fullname_, account_, email_, sex_, status_, create_time_, create_by_) " +
                     "VALUES (?, ?, ?, ?, ?, ?, ?, ?)";

        Long uuid = IdUtil.getSnowflakeNextId();
        String fullname = accountResponse.getName();
        int flag1 = obpmJdbcTemplate.update(
                sql,
                uuid,
                fullname,
                account,
                email,
                sex,
                1,
                now,
                "417137619324108800"
        );

        AdminUserDO entity = new AdminUserDO();
        entity.setId(uuid);
        entity.setUsername(account);
        entity.setNickname(fullname);
        entity.setEmail(email);
        entity.setSex(sex);
        entity.setStatus(1);
        entity.setCreateTime(now);
        int flag2 = userMapper.insert(entity);

        String sql2 = "INSERT INTO org_relation (id_, user_id_, is_master_, role_id_, status_, type_, create_time_, create_by_) " +
                      "VALUES (?, ?, ?, ?, ?, ?, ?, ?)";

        int flag3 = obpmJdbcTemplate.update(
                sql2,
                IdUtil.getSnowflakeNextId(),
                uuid,
                0,
                "452309440906919937",
                1,
                "userRole",
                now,
                "417137619324108800"
        );

        if (flag1 < 0 || flag2 < 0 || flag3 < 0) {
            throw new ServiceException(500, "未知错误，请联系管理员");
        }
    }


    public void insertUserToEapDb(LoginResponse.Account accountResponse) {
        LocalDateTime now = LocalDateTime.now();
        String sexCode = accountResponse.getSexCode();
        Integer sex = null;
        if (sexCode != null) {
            if (sexCode.equals("MALE")) {
                sex = 1;
            } else {
                sex = 2;
            }
        }
        String account = accountResponse.getCode();

        String email = queryEmailByAccount(account);
        Long uuid = IdUtil.getSnowflakeNextId();
        String fullname = accountResponse.getName();
        AdminUserDO entity = new AdminUserDO();
        entity.setId(uuid);
        entity.setUsername(account);
        entity.setNickname(fullname);
        entity.setEmail(email);
        entity.setSex(sex);
        entity.setStatus(1);
        entity.setCreateTime(now);
        int flag2 = userMapper.insert(entity);
        if ( flag2 < 0) {
            throw new ServiceException(500, "未知错误，请联系管理员");
        }
    }

    public String queryEmailByAccount(String account) {
        if (StringUtils.isBlank(account)) {
            return null;
        }

        try {
            String url = emailApiConfig.getBaseUrl() + account;
            log.info("开始查询工号：{} 的邮箱信息，请求URL：{}", account, url);

            String response = HttpRequest.get(url)
                    .header("X-Sec-App-Auth-Key", emailApiConfig.getAuthKey())
                    .timeout(emailApiConfig.getTimeout())
                    .execute()
                    .body();

            log.debug("email-API响应：{}", response);

            // 解析响应
            EmailApiResponse apiResponse = JSONUtil.toBean(response, EmailApiResponse.class);

            if (apiResponse == null || !Boolean.TRUE.equals(apiResponse.getSuccess())) {
                log.warn("API调用失败，工号：{}，响应：{}", account, response);
                return null;
            }

            // 检查返回的数据
            if (apiResponse.getData() == null) {
                log.info("未找到工号：{} 对应的用户信息", account);
                return null;
            }

            String email = apiResponse.getData().getEmail();
            if (StringUtils.isBlank(email)) {
                log.info("工号：{} 的邮箱为空", account);
                return null;
            }

            log.info("成功获取工号：{} 的邮箱：{}", account, email);
            return email.trim();

        } catch (Exception e) {
            log.error("查询工号：{} 的邮箱时发生异常", account, e);
            return null;
        }
    }

    private AdminUserDO authenticateByToken(com.alibaba.fastjson.JSONObject reqVO) {

        return null;
    }

    private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) {
        // 插入登陆日志
        createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS);
        // 创建访问令牌
        OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, username, getUserType().getValue(),
                OAuth2ClientConstants.CLIENT_ID_DEFAULT, null);
        // 构建返回结果
        return AuthConvert.INSTANCE.convert(accessTokenDO);
    }


    /**
     * 查询Obpm用户信息
     *
     * @param username
     * @param withPassword
     * @return
     */
    public JSONObject queryObpmUser(String username, boolean withPassword) {
        JSONObject jsonUser = obmpClientService.queryUserInfo(username, withPassword);
        return jsonUser;
    }

    /**
     * 根据obpm用户创建本地用户
     *
     * @param jsonUser
     * @return
     */
    protected AdminUserDO createAdminUser(JSONObject jsonUser, String creator) {
        return obpmUserService.createAdminUser(jsonUser, creator);
    }

    @Transactional
    public OAuth2AccessTokenDO createOAuth2CustToken(String accessToken, Integer accessTokenValiditySeconds) {
        OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.getAccessToken(accessToken);
        // 构建返回结果
        return createOAuth2CustToken(accessTokenDO, accessTokenValiditySeconds);
    }

    private OAuth2AccessTokenDO createOAuth2CustToken(OAuth2AccessTokenDO accessTokenDO, Integer accessTokenValiditySeconds) {
        OAuth2AccessTokenDO custTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
                .setUserId(accessTokenDO.getUserId()).setUserType(accessTokenDO.getUserType())
                .setUserKey(accessTokenDO.getUserKey())
                .setUserInfo(accessTokenDO.getUserInfo())
                .setClientId(accessTokenDO.getClientId()).setScopes(accessTokenDO.getScopes())
                .setRefreshToken(accessTokenDO.getRefreshToken())
                .setExpiresTime(LocalDateTime.now().plusSeconds(accessTokenValiditySeconds));
        custTokenDO.setTenantId(TenantContextHolder.getTenantId());
        oauth2AccessTokenMapper.insert(custTokenDO);
        // 记录到 Redis 中
        oauth2AccessTokenRedisDAO.set(custTokenDO);
        return custTokenDO;
    }


    private static String generateAccessToken() {
        return IdUtil.fastSimpleUUID();
    }


    public void countIpPwdWrong() {
        String ip = ServletUtils.getClientIP();
        if (StringUtils.isNotBlank(ip)) {
            countIpPwdWrong(ip);
        }
    }

    public void countIpPwdWrong(String ip) {
        Long count;
        if (!stringRedisTemplate.hasKey("IP_counts")) {
            // 不存在KEY，先添加，在设置过期策略
            count = stringRedisTemplate.opsForHash()
                    .increment("IP_counts", ip, 1L);
            stringRedisTemplate.boundHashOps("IP_counts").expire(30, TimeUnit.MINUTES);
        } else {
            // 直接添加
            count = stringRedisTemplate.opsForHash()
                    .increment("IP_counts", ip, 1L);

        }
        if (count > 13L) {
            if (!stringRedisTemplate.hasKey("IP_blacklists")) {
                stringRedisTemplate.opsForSet().add("IP_blacklists", ip);
                stringRedisTemplate.boundHashOps("IP_blacklists").expire(30, TimeUnit.MINUTES);
            } else {
                stringRedisTemplate.opsForSet().add("IP_blacklists", ip);
            }
        }

    }

    public Boolean existInBlackList(String ip) {
        if (stringRedisTemplate.hasKey("IP_blacklists")) {
            return stringRedisTemplate.boundSetOps("IP_blacklists").isMember(ip);
        } else {
            return false;
        }
    }

    public void clearIPCount(String ip) {
        stringRedisTemplate.opsForHash().delete("IP_counts", ip);
        stringRedisTemplate.opsForSet().remove("IP_blacklists", ip);
    }

}
